﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using CSJ2K.Util.IO;

namespace CSJ2K.Jpc.Segments
{
    /// <summary>Quantization default (QCD)</summary>
    /// <comment>
    /// Function: Describes the quantization default used for compressing all components not
    ///      defined by a QCC marker segment. The parameter values can be overridden for an 
    ///      individual component by a QCC marker segment in either the main or tile-part header.
    /// Usage: Main and first tile-part header of a given tile. Shall be one and only one in the 
    ///      main header. May be at most one for all tile-part headers of a tile. If there are 
    ///      multiple tile-parts for a tile, and this marker segment is present, it shall be 
    ///      found only in the first tile-part (TPsot = 0). When used in the tile-part header 
    ///      it overrides the main QCD and the main QCC for the specific component. Thus, the 
    ///      order of precedence is the following: 
    ///          Tile-part QCC > Tile-part QCD > Main QCC > Main QCD 
    ///      where the "greater than" sign, >, means that the greater overrides the lessor marker 
    ///      segment. 
    /// Length: Variable depending on the number of quantized elements. 
    /// </comment>
    public class QCD : Segment
    {
        public enum QuantizationStyles : byte
        {
            None=0x00,
            ScalarDerived=0x01,
            ScalarExpounded=0x02,
            StyleMask=0x1F,
            GuardMask=0xE0
        }

        /// <summary>Quantization style for all components.</summary>
        public QuantizationStyles S;

        /// <summary>Quantization step size value for the ith sub-band in the defined order. (8 or 16 bit per element depending on Sqcd.)</summary>
        public ushort[] SP; 

        public QCD(Stream source)
            : this(source, (Markers)StreamUtil.ReadBUInt16(source)) { }
        public QCD(Stream source, Markers marker)
            : base(source, marker, true)
        {
            if (marker != Markers.QCD) throw (new ApplicationException("Attempting to create segment of the wrong type."));

            S = (QuantizationStyles)StreamUtil.ReadOctet(source);
            Debug.WriteLine("Quantization Style:");
            Debug.Indent();
            Debug.WriteLineIf(((S & QuantizationStyles.StyleMask) == QuantizationStyles.None), "No quantization");
            Debug.WriteLineIf(((S & QuantizationStyles.StyleMask) == QuantizationStyles.ScalarDerived), "Scalar derived");
            Debug.WriteLineIf(((S & QuantizationStyles.StyleMask) == QuantizationStyles.ScalarExpounded), "Scalar expounded");
            Debug.WriteLine(((byte)S >> 5) + " Guard bits");
            Debug.Unindent();

            Debug.WriteLine("Quantization Step Size:");
            Debug.Indent();
            byte[] buffer = StreamUtil.ReadBytes(source, Length - 3);
            if ((S & QuantizationStyles.StyleMask) == QuantizationStyles.None)
            {
                SP = new ushort[buffer.Length];
                for (int i = 0; i < buffer.Length; i++)
                {
                    SP[i] = buffer[i];
                    Debug.WriteLine("Sub-band " + i + ": Exponent " + (SP[i]>>3));
                }
            }
            else
            {
                SP = new ushort[buffer.Length / 2];
                byte[] buf = new byte[2];
                for (int i = 0; i < buffer.Length; i += 2)
                {
                    buf[0] = buffer[i+1];
                    buf[1] = buffer[i];
                    SP[i/2] = BitConverter.ToUInt16(buf, 0);
                    Debug.WriteLine("Sub-band " + (i / 2) + ": Mantissa " + (SP[i / 2] & 0x07FF) + " Exponent " + (SP[i / 2] >> 11));
                }
            }
            Debug.Unindent();
            Debug.WriteLine("Subbands: " + SP.Length);
        }
    }
}
